//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Reflection;
using System.Windows;
using System.Windows.Media;
using OpenLS.Core;
using OpenLS.Spreadsheet.Collections;
using OpenLS.Spreadsheet.Internals;

namespace OpenLS.Spreadsheet
{
    public abstract class Dimension : object, INotifyPropertyChanged
    {
        protected Dimension(Worksheet ws)
        {
            _worksheet = ws;
        }
        public override string ToString()
        {
            return Label;
        }

        private ChildGroup _group;

        internal ChildGroup Group
        {
            get { return _group; }
            set
            {
                Contract.Requires(value == null || value.Contains(this));
                _group = value;
                if (_group != null)
                    ApplyGroupVisibility();
                else
                    IsVisible = true;
            }
        }

        internal void ApplyGroupVisibility()
        {
            Contract.Requires(Group != null);
            IsVisible = Group.IsVisibleAndExpanded;
        }

        internal abstract int MaxValidOffsetIndex { get; set; }


        internal abstract Dimension GetOffseted(CellRelativeAddress address);
        // double _offset = -1;

        internal abstract double Offset { get; }
        /*        {
                    get
                    {
                        while (!IsValidOffset)
                        {
                            CalculateOffsets();
                        }
                        return _offset;
                    }*/
        /*  set
          {
              if (_offset == value)
                  return;
              _offset = value;
              NotifyPropertyChanged("Offset");

          }*/
        /*   } */
#if DEBUG
        private bool IsValidOffset
        {
            get { throw new NotImplementedException(); //  return Index <= MaxValidOffsetIndex && _offset != -1;
            }
        }
#endif

        private readonly Int32Dictionary<Cell> _cells = new Int32Dictionary<Cell>();

        internal int CellCount
        {
            get { return _cells.Count; }
        }

        internal IEnumerable<int> CellKeys
        {
            get { return _cells.Keys; }
        }
        /// <summary>
        /// Non distinct
        /// </summary>
        /// <returns></returns>
        internal IEnumerable<Cell> GetCells()
        {
            return _cells.Values;
        }

        internal Cell CellAt(int index)
        {
            return _cells[index];
        }

        internal void AddCellToDictionary(int index, Cell cell)
        {
            _cells.Add(index, cell);
        }

        protected void RemoveCellFromDictionary(int index)
        {
            _cells.Remove(index);
        }

        internal Cell GetItem(int i)
        {
            Cell result;
            if (!_cells.TryGetValue(i, out result))
                return null;
            return result;
        }

        public static bool operator <=(Dimension dim1, Dimension dim2)
        {
            return dim1._index <= dim2._index; //\\check consistent
        }

        public static bool operator >=(Dimension dim1, Dimension dim2)
        {
            return dim1._index >= dim2._index; //\\check consistent
        }

        public int Count
        {
            get { return _cells.Count; }
        }

        public string FullLabel
        {
            get { return Worksheet.Name + "!" + Label; }
        }

        public  string Label
        {
            get
            {
                Contract.Assert(!IsDeleted);
                return UnsafeLabel;
            }
        }
        internal abstract string UnsafeLabel { get; }

        internal void ClearStyleRecord()
        {
            FormattingRecord = null;
        }

        private CellFormattingRecord _formattingRecord;

        internal void SetFormattingRecord(CellFormattingRecord formattingRecord)
        {
            _formattingRecord = formattingRecord;
        }

        internal CellFormattingRecord FormattingRecord
        {
            get { return _formattingRecord; }
            private set
            {
                Contract.Requires(value == null || Workbook.StyleSheet.ContainsRecord(value));
                if (Workbook.UndoEnabled)
                {
                    Workbook.AddUndoAction(FormattingRecord, r => FormattingRecord = r);
                }

                _formattingRecord = value;
                NotifyPropertyChanged(CellProperties.Background);
                //NotifyPropertyChanged(CellProperty.Borders);
                //\\ NotifyPropertyChanged(CellProperty.FontFamily);
                NotifyPropertyChanged(CellProperties.FontSize);
                NotifyPropertyChanged(CellProperties.FontStyle);
                NotifyPropertyChanged(CellProperties.FontUnderline);
                NotifyPropertyChanged(CellProperties.FontWeight);
                NotifyPropertyChanged(CellProperties.Foreground);
                NotifyPropertyChanged(CellProperties.HorizontalAlignment);
                NotifyPropertyChanged(CellProperties.Indentation);
                NotifyPropertyChanged(CellProperties.IsLocked);
                NotifyPropertyChanged(CellProperties.Format);
                NotifyPropertyChanged(CellProperties.TextWrapping);
                NotifyPropertyChanged(CellProperties.VerticalAlignment);
            }
        }

        internal void SetStyleRecordSafe(CellFormattingRecord styleRecord)
        {
            if (styleRecord == null)
            {
                ClearStyleRecord();
                return;
            }
            FormattingRecord = Workbook.StyleSheet.LookupNewCellFormattingRecord(styleRecord);
        }

        internal Format Format
        {
            get
            {
                if (FormattingRecord != null)
                    return FormattingRecord.Format;
                return null;
            }
            set
            {
                CellFormattingRecord r = FormattingRecord != null
                                             ? FormattingRecord.Clone()
                                             : new CellFormattingRecord();
                r.Format = value;
                FormattingRecord = Worksheet.Workbook.StyleSheet.LookupNewCellFormattingRecord(r);
            }
        }

        public Fill Background
        {
            get
            {
                if (_formattingRecord == null)
                    return null;
                return _formattingRecord.Background;
            }
        }

        internal VerticalCellAlignment VerticalAlignment
        {
            get
            {
                if (_formattingRecord == null)
                    return VerticalCellAlignment.Bottom;
                return _formattingRecord.VerticalAlignment;
            }
        }

        internal HorizontalCellAlignment? HorizontalAlignment
        {
            get
            {
                if (_formattingRecord == null)
                    return null;
                return _formattingRecord.HorizontalAlignment;
            }
        }

        internal TextWrapping TextWrapping
        {
            get
            {
                if (_formattingRecord == null)
                    return TextWrapping.NoWrap;
                return _formattingRecord.TextWrapping;
            }
        }

        internal double Indentation
        {
            get
            {
                if (_formattingRecord == null)
                    return 0;
                return _formattingRecord.Indentation;
            }
        }

        internal bool IsLocked
        {
            get
            {
                if (_formattingRecord == null)
                    return false;
                return _formattingRecord.IsLocked;
            }
        }

        internal SpreadsheetColor Foreground
        {
            get
            {
                if (_formattingRecord == null)
                    return null;
                return _formattingRecord.Foreground;
            }
        }

        internal FontWeight FontWeight
        {
            get
            {
                if (_formattingRecord == null)
                    return FontWeights.Normal;
                return _formattingRecord.FontWeight;
            }
        }

        internal FontStyle FontStyle
        {
            get
            {
                if (_formattingRecord == null)
                    return FontStyles.Normal;
                return _formattingRecord.FontStyle;
            }
        }

        internal UnderlineType FontUnderline
        {
            get
            {
                if (_formattingRecord == null)
                    return UnderlineType.None;
                return _formattingRecord.FontUnderline;
            }
        }

        internal double FontSize
        {
            get
            {
                if (_formattingRecord == null)
                    return 11.0; //\\b
                return _formattingRecord.FontSize;
            }
        }

        internal FontFamily FontFamily
        {
            get
            {
                if (_formattingRecord == null)
                    return null;
                return _formattingRecord.FontFamily;
            }
        }

        internal void SetCellValue(CellProperty p, object value)
        {
            //\\this.Workbook.AddUndoAction( this, d => d._styleRecord = _styleRecord);
            CellFormattingRecord r = _formattingRecord != null ? _formattingRecord.Clone() : new CellFormattingRecord();
            r.SetCellValue(p, value);
            FormattingRecord = Worksheet.Workbook.StyleSheet.LookupNewCellFormattingRecord(r);
            NotifyPropertyChanged(p.ToString()); //\\
        }

        internal object GetCellValue(CellProperty p)
        {
            Contract.Ensures(!(Contract.Result<object>() is Exception));
            if (_formattingRecord == null)
                return Spreadsheet.FormattingRecord.GetDefault(p);
            return _formattingRecord.GetCellValue(p);
        }


        internal abstract bool GetIsSelected();

        public bool IsSelected
        {
            get { return GetIsSelected(); }
        }

        private bool isDeleted;
        internal abstract bool IsEmpty { get; }

        public bool IsDeleted
        {
            get { return isDeleted; }
            set
            {
                Debug.WriteLine("Deleting " + UnsafeLabel);
                if (isDeleted == value)
                    return;
                if (Deleted != null)
                    Deleted(this, null);
                isDeleted = value;
                if (!isDeleted)
                {
                    foreach (Cell cell in GetNonEmptyCells())
                        cell.Undelete();
                    if (Group != null)
                        Group.OnDimensionDeleted(this);
                }
            }
        }

        internal event EventHandler Deleted;

        /// <summary>
        /// Get the length of the dimension in pixels. This represents the height for a row and the width for a column.
        /// </summary>
        public abstract double Length { get; }

        private readonly Worksheet _worksheet;

        ///<summary>
        /// Get the worksheet which contains the row or column.
        ///</summary>
        public Worksheet Worksheet
        {
            get { return _worksheet; }
        }

        private int _index;
#if DEBUG
        [ContractInvariantMethod]
        private void ObjectInvariant()
        {
            Contract.Invariant(_index >= 0);
            Contract.Invariant(Index >= 0);
        }

#endif


        public int Index
        {
            get
            {
                Contract.Ensures(Contract.Result<int>() >= 0);
                return _index;
            }
            internal set
            {
                Contract.Requires(value >= 0);
                Contract.Requires(!(this is Row) | (value <= Worksheet.MaxRowIndex));
                Contract.Requires(!(this is Column) | (value <= Worksheet.MaxColumnIndex));
                _index = value;
                NotifyPropertyChanged("Index");
            }
        }


        internal bool Contains(Cell cell)
        {
            foreach (Cell myCell in GetNonEmptyCells())
                if (myCell == cell)
                    return true;
            return false;
        }

        #region IEnumerable<Cell> Members

        internal IEnumerable<Cell> GetNonEmptyCells()
        {
            foreach (Cell cell in _cells.Values)
                yield return cell;
        }

        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

       /* internal void NotifyPropertyChanged(CellProperty s)
        {
            Worksheet.Workbook.Document.SetDirty();
            NotifyPropertyChanged(s.ToString());
        }
        */
        internal void NotifyPropertyChanged(string propertyName)
        {
            Contract.Requires(
                GetType().GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) !=
                null);
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            if (Worksheet != null)
                Worksheet.NotifyDimensionPropertyChanged(this, propertyName);
        }

        #endregion

        internal void InvalidateOffset()
        {
            MaxValidOffsetIndex = Math.Min(MaxValidOffsetIndex, Index - 1);
        }

        internal void SetDimensionProperty(DimensionProperty prop, object value)
        {
            switch (prop)
            {
                case DimensionProperty.Visibility:
                    IsVisible = (bool) value;
                    break;
                default:
                    throw new NotSupportedException(prop.ToString());
            }
        }

        internal abstract bool IsHidden { get; set; }

        ///<summary>
        /// Get or set the visibility of the row or column
        ///</summary>
        public bool IsVisible
        {
            get { return !IsHidden; }
            set
            {
                if (IsVisible == value)
                    return;
                using (Worksheet.CreateUndoScope(LengthSrid))
                {
                    if (Worksheet.Workbook.UndoEnabled)
                        Worksheet.Workbook.UndoManager.Add(new DimensionPropertyUndoUnit(this,
                                                                                         DimensionProperty.Visibility,
                                                                                         IsVisible));
                    IsHidden = !value;
                    this.OnIsVisibleChanged();
                }
            }
        }

        internal abstract void OnIsVisibleChanged();

        internal abstract string LengthSrid { get; }

        [Obsolete("Use the style property instead")]
        public void SetStyle(CellStyle style)
        {
            using (Workbook.CreateUndoScope(StringTable.Style))
            {
                CellFormattingRecord r = Spreadsheet.FormattingRecord.CreateFromParent(style.Record);
                FormattingRecord = Workbook.StyleSheet.LookupNewCellFormattingRecord(r);
            }
        }

        /// <summary>
        /// Get or set the style of the row or column
        /// </summary>
        public CellStyle Style
        {
            get
            {
                if (FormattingRecord == null)
                    return null;
                if (FormattingRecord.Parent == null)
                    return null;
                return FormattingRecord.Parent.Style;
            }
            set
            {
                Contract.Ensures(Style == value);
                using (Workbook.CreateUndoScope(StringTable.Style))
                {
                    if (value == null)
                    {
                        if (FormattingRecord == null)
                            return;
                        CellFormattingRecord r = FormattingRecord.Clone();
                        r.SetParent(null);
                        FormattingRecord = Workbook.StyleSheet.LookupNewCellFormattingRecord(r);

                        return;
                    }
                    else
                    {
                        CellFormattingRecord r = Spreadsheet.FormattingRecord.CreateFromParent(value.Record);
                        FormattingRecord = Workbook.StyleSheet.LookupNewCellFormattingRecord(r);
                    }
                }
            }
        }

        internal Workbook Workbook
        {
            get { return Worksheet.Workbook; }
        }


        internal bool IsCollapsed
        {
            get
            {
                if (Group != null)
                    return !Group.IsExpanded;
                return false;
            }
            set
            {
                if (Group != null)
                    Group.IsExpanded = !value;
            }
        }

        public bool IsExpanded
        {
            get { return !IsCollapsed; }
            set { IsCollapsed = !value; }
        }

        public int OutlineLevel
        {
            get
            {
                if (Group == null)
                    return 0;
                return Group.Depth;
            }
            //  set { outlineLevel = value; }
        }

        internal void SetBorder(Action<Borders> action)
        {
            Borders borders = TransformBorders(action);
            SetCellValue(CellProperty.Borders, borders);
        }

        private Borders TransformBorders(Action<Borders> action)
        {
            Borders result;
            if (Borders == null)
                result = new Borders();
            else
                result = Borders.Clone();
            action(result);
            return result;
        }
        internal void AdviseMinMax(int colMic, int colMac)
        {
            this._cells.AdviseMinMax(colMic, colMac);
        }

        internal Borders Borders
        {
            get
            {
                if (FormattingRecord == null)
                    return null;
                return FormattingRecord.Borders;
            }
        }

        internal abstract Dimension GetPrevious();
    }
}